package apig.servlet;

import java.sql.*;
import java.util.*;

import apig.client.serviface.*;
import apig.core.*;
import apig.servlet.access.*;
import apig.servlet.access.policy.*;

import com.google.gwt.user.server.rpc.*;

public class ApigServiceProxy extends RemoteServiceServlet implements
		ApigService {

	private ApigServiceImpl impl = new ApigServiceImpl();

	private static final String GET_CODES_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".getCodes";
	private static final String GET_STUD_DISCIPLINES_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".getStudDisciplines";
	private static final String GET_DISCIPLINE_LABS_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".getDisciplineLabs";
	private static final String SUBSCRIBE_ON_SYLLABUS_UPDATE_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".subscribeOnSyllabusUpdate";
	private static final String DECLINE_SYLLABUS_UPDATE_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".declineSyllabusUpdates";
	private static final String ASSIGN_DISCIPLINE_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".assignDiscipline";
	private static final String CREATE_DISCIPLINE_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".createDiscipline";
	private static final String REMOVE_DISCIPLINE_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".removeDiscipline";
	private static final String CREATE_LAB_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".createLab";
	private static final String REMOVE_LAB_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".removeLab";
	private static final String GET_USER_DATA_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".getUserData";
	private static final String DO_LOGIN_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".doLogin";
	private static final String DO_LOGOUT_METHOD = ApigServiceProxy.class
			.getCanonicalName() + ".doLogout";

	public ApigServiceProxy() {
		ServiceAccessManager sam = ServiceAccessManager.getManager();
		sam.registerService(GET_CODES_METHOD, new ProfAndOwnerIdAccessPolicy());

		sam.registerService(GET_STUD_DISCIPLINES_METHOD,
				new ProfAndOwnerIdAccessPolicy());
		sam.registerService(GET_DISCIPLINE_LABS_METHOD,
				new AllPermittedPolicy());
		sam.registerService(SUBSCRIBE_ON_SYLLABUS_UPDATE_METHOD,
				new ProfAndOwnerIdAccessPolicy());
		sam.registerService(DECLINE_SYLLABUS_UPDATE_METHOD,
				new AllPermittedPolicy());
		sam.registerService(ASSIGN_DISCIPLINE_METHOD, new ProfAccessPolicy());
		sam.registerService(CREATE_DISCIPLINE_METHOD, new ProfAccessPolicy());
		sam.registerService(REMOVE_DISCIPLINE_METHOD, new ProfAccessPolicy());
		sam.registerService(CREATE_LAB_METHOD, new ProfAccessPolicy());
		sam.registerService(REMOVE_LAB_METHOD, new ProfAccessPolicy());
		sam.registerService(GET_USER_DATA_METHOD, new AllPermittedPolicy());
		sam.registerService(DO_LOGIN_METHOD, new AllPermittedPolicy());
		sam.registerService(DO_LOGOUT_METHOD, new AllPermittedPolicy());

	}

	@Override
	public final HashMap<Group, List<Student>> getAllStuds()
			throws AccessException, DatabaseException {

		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.getAllStuds(c);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final List<Code> getCodes(CodeQuery cq) throws DatabaseException,
			AccessException {

		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.getCodes(c, cq);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final HashMap<Integer, List<Hypo>> getHypos(int codeId)
			throws DatabaseException, AccessException {
		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.getHypos(c, codeId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final String getCodeOwner(int codeId) throws DatabaseException,
			AccessException {
		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.getCodeOwner(c, codeId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final Code getCode(int codeId) throws DatabaseException,
			AccessException {
		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.getCode(c, codeId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final List<Discipline> getStudDisciplines(int studId)
			throws DatabaseException, AccessException {
		try (Connection c = ServerThings.getThings().getConnection();
				PreparedStatement getDisciplinesStatement = c
						.prepareStatement("select disciplines.discipline_id, "
								+ "disciplines.owner_id, disciplines.name "
								+ "from disciplines, discipline_orders, students, groups "
								+ "where disciplines.discipline_id = discipline_orders.discipline_id "
								+ "and discipline_orders.group_id = students.group_id "
								+ "and students.student_id = ?");) {
			UserData userRequesterData = ApigServiceImpl.getUserData(c,
					getThreadLocalRequest().getSession().getId());
			if (!ServiceAccessManager.getManager().checkAccess(
					userRequesterData, GET_STUD_DISCIPLINES_METHOD,
					new UserIdRD(studId))) {
				throw new AccessException("Access denied.");
			}
			return impl.getStudDisciplines(c, studId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final List<Lab> getDisciplineLabs(int discId)
			throws DatabaseException, AccessException {
		try (Connection c = ServerThings.getThings().getConnection();) {
			// UserData userRequesterData = UserServiceImpl.getUserData(c,
			// getThreadLocalRequest().getSession().getId());
			UserData ud = null;
			if (!ServiceAccessManager.getManager().checkAccess(ud,
					GET_DISCIPLINE_LABS_METHOD, null)) {
				throw new AccessException("Access denied.");
			}
			return impl.getDisciplineLabs(c, discId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final void assignDiscipline(int discId, int groupId, boolean assign)
			throws DatabaseException, AccessException {
		try (Connection c = ServerThings.getThings().getConnection();

				PreparedStatement insertStatement = c
						.prepareStatement("insert into discipline_orders values (?,?)");) {
			// UserData userRequesterData = UserServiceImpl.getUserData(c,
			// getThreadLocalRequest().getSession().getId());
			UserData userRequesterData = null;
			if (!ServiceAccessManager.getManager().checkAccess(
					userRequesterData, ASSIGN_DISCIPLINE_METHOD, null)) {
				throw new AccessException("Access denied.");
			}
			insertStatement.setInt(1, discId);
			insertStatement.setInt(2, groupId);
			insertStatement.executeUpdate();
			c.commit();
			// sum.notifyGroupSyllabusChange(groupId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final void createDiscipline(Discipline d, int ownerId)
			throws DatabaseException, AccessException {
		try (Connection c = ServerThings.getThings().getConnection();) {
			UserData userRequesterData = ApigServiceImpl
					.getUserData(getThreadLocalRequest().getSession().getId());
			if (!ServiceAccessManager.getManager().checkAccess(
					userRequesterData, CREATE_DISCIPLINE_METHOD, null)) {
				throw new AccessException("Access denied.");
			}
			impl.createDiscipline(c, d, ownerId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final void removeDiscipline(int discId) throws DatabaseException,
			AccessException {
		try (Connection c = ServerThings.getThings().getConnection();) {
			// UserData userRequesterData = UserServiceImpl.getUserData(c,
			// getThreadLocalRequest().getSession().getId());
			UserData userRequesterData = null;
			if (!ServiceAccessManager.getManager().checkAccess(
					userRequesterData, REMOVE_DISCIPLINE_METHOD, null)) {
				throw new AccessException("Access denied.");
			}
			impl.removeDiscipline(c, discId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final void createLab(Lab l) throws DatabaseException,
			AccessException {
		try (Connection c = ServerThings.getThings().getConnection();
				PreparedStatement groupsStatement = c.prepareStatement(""
						+ "select group_id " + "from disciplines "
						+ "where discipline_id = ?");
				PreparedStatement insertStatement = c
						.prepareStatement("insert into labs values (nextval('labs'), ?, ?)");) {
			// UserData userRequesterData = UserServiceImpl.getUserData(c,
			// getThreadLocalRequest().getSession().getId());
			UserData userRequesterData = null;
			if (!ServiceAccessManager.getManager().checkAccess(
					userRequesterData, CREATE_LAB_METHOD, null)) {
				throw new AccessException("Access denied.");
			}
			impl.createLab(c, l);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final void removeLab(int labId) throws DatabaseException,
			AccessException {
		try (Connection c = ServerThings.getThings().getConnection();) {
			// UserData userRequesterData = UserServiceImpl.getUserData(c,
			// getThreadLocalRequest().getSession().getId());
			UserData userRequesterData = null;
			if (!ServiceAccessManager.getManager().checkAccess(
					userRequesterData, REMOVE_LAB_METHOD, null)) {
				throw new AccessException("Access denied.");
			}
			impl.removeLab(c, labId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final HashMap<Discipline, List<Lab>> getStudLabs(int studId)
			throws AccessException, DatabaseException {

		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.getStudLabs(c, studId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public final UserData getUserData() throws DatabaseException,
			AccessException {
		if (!ServiceAccessManager.getManager().checkAccess(null,
				GET_USER_DATA_METHOD, null)) {
			throw new AccessException("Access denied.");
		}
		String sessionId = getThreadLocalRequest().getSession().getId();

		UserData ud = null;
		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.getUserData(c, sessionId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	/**
	 * if user is already logged in and sends a request to 
	 * log in again with some account (no matter the other
	 * or the current), he is automatically logged out and the logged in
	 * 
	 * Always returns true
	 */
	public final Boolean doLogin(String login, String pass)
			throws DatabaseException, AccessException {
		if (!ServiceAccessManager.getManager().checkAccess(null,
				DO_LOGIN_METHOD, null)) {
			throw new AccessException("Access denied.");
		}
		String sessionId = getThreadLocalRequest().getSession().getId();

		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.doLogin(c, login, pass, sessionId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	/**
	 * Always returns truth
	 */
	public final Boolean doLogout() throws DatabaseException, AccessException {
		if (!ServiceAccessManager.getManager().checkAccess(null,
				DO_LOGOUT_METHOD, null)) {
			throw new AccessException("Access denied.");
		}
		String sessionId = getThreadLocalRequest().getSession().getId();
		try (Connection c = ServerThings.getThings().getConnection();) {
			return impl.doLogout(c, sessionId);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	private String getLogin(Connection c, String sessionId)
			throws SQLException, DatabaseException {
		try (PreparedStatement s = c
				.prepareStatement("select user_session_data.user_id from user_session_data "
						+ "where user_session_data.session_id = ?");) {
			s.setString(1, sessionId);
			ResultSet rs = s.executeQuery();
			if (!rs.next()) {
				return null;
			} else {
				return Integer.toString(rs.getInt(1));
			}
		}
	}
}
